package main;

/**
 * Class that is representing a vector.
 * 
 * @author Michal Stojek
 */
public class Vector {
	private double x;
	private double y;
	
	/**
	 * Creates a new instance of a class Vector with initial values [X, Y].
	 * 
	 * @param x	starting value of X of a vector specified by [X, Y]
	 * @param y	starting value of Y of a vector specified by [X, Y]
	 */
	public Vector(double x, double y) {
		this.x = x;
		this.y = y;
	}
	
	/**
	 * Creates a new instance of a class Vector that is a vector starting at point A
	 * and ending at point B.
	 * 
	 * @param a point A, starting point of a vector
	 * @param b point B, ending point of a vector
	 */
	public Vector(Point a, Point b) {
		this.x = b.getX()-a.getX();
		this.y = b.getY()-a.getY();
	}
	
	/**
	 * Returns an exact copy of the vector that the method is called on.
	 * 
	 * @return copy of a vector
	 */
	public Vector copy() {
		return new Vector(x, y);
	}
	
	/**
	 * Returns value of X of a vector specified by [X, Y].
	 * 
	 * @return value of X of a vector specified by [X, Y]
	 */
	public double getX() {
		return x;
	}
	
	/**
	 * Sets value of X of a vector specified by [X, Y].
	 * 
	 * @param x new value of X of a vector specified by [X, Y]
	 */
	public void setX(double x) {
		this.x = x;
	}
	
	/**
	 * Returns value of Y of a vector specified by [X, Y].
	 * 
	 * @return value of Y of a vector specified by [X, Y]
	 */
	public double getY() {
		return y;
	}
	
	/**
	 * Sets value of X of a vector specified by [X, Y].
	 * 
	 * @param x new value of X of a vector specified by [X, Y]
	 */
	public void setY(double y) {
		this.y = y;
	}
	
	/**
	 * Returns a new vector which is a sum of a vector V and 
	 * a vector this method is being called on
	 * 
	 * @param v	vector V which we are adding to a vector this method is being called on 
	 * @return a new vector which is a sum of V and a vector this method is being called on
	 */
	public Vector add(Vector v) {
		return new Vector(x+v.getX(), y+v.getY());
	}
	
	/**
	 * Returns length of a vector this method is being called on.
	 * 
	 * @return length of a vector this method is being called on
	 */
	public double length() {
		return Math.sqrt(x*x+y*y);
	}
	
	/**
	 * Returns a new vector which is a result of scaling a vector 
	 * this method is being called on by a constant t.
	 * 
	 * @param t	constant by which we want to scale the vector
	 * @return new, scaled vector
	 */
	public Vector scale(double t) {
		return new Vector(x*t, y*t);
	}
	
	/**
	 * Returns a new vector which is a result of scaling a vector 
	 * this method is being called on to length 1.
	 * 
	 * @return a new, unit vector
	 */
	public Vector unit() {
		return new Vector(x/length(), y/length());
	}
	
	/**
	 * Returns a new vector which is a result of rotating clockwise
	 * a vector this method is being called on.
	 * 
	 * @param angle	angle we want to rotate the vector by, in degrees
	 * @return a new, rotated vector
	 */
	public Vector rotate(double angle) {
		angle = Math.toRadians(angle);
		return new Vector(x*Math.cos(angle)+y*Math.sin(angle), -1*x*Math.sin(angle)+y*Math.cos(angle));
	}
	
	/**
	 * Returns a dot product of 2 given vectors A and B.
	 * 
	 * @param a	vector A
	 * @param b	vector B
	 * @return	dot product of vectors A and B
	 */
	public static double dotProduct(Vector a, Vector b) {
		return a.getX()*b.getX()+a.getY()*b.getY();
	}
	
	/**
	 * Returns a vector that is perpendicular to the vector
	 * this method is being called on.
	 * 
	 * @return	a new, perpendicular vector
	 */
	public Vector perpendicular() {
		return new Vector(-y, x);
	}
	
	/**
	 * Returns a vector of same magnitude and direction but different sense
	 * as a vector this method is being called on.
	 * 
	 * @return	a new, inverted vector
	 */
	public Vector invert() {
		return new Vector(-x, -y);
	}
	
	/**
	 * Returns a number of degrees that a vector A has to be rotated clockwise
	 * so that it has the same direction and sense as vector B.
	 * 
	 * @param a	vector A
	 * @param b	vector B
	 * @return	an angle between vectors A and B
	 */
	public static double angle(Vector a, Vector b) {
		double cosAlpha = dotProduct(a.unit(), b.unit());
		double alpha = Math.acos(cosAlpha);
		alpha = Math.toDegrees(alpha);
		if (dotProduct(a.rotate(alpha), b) > dotProduct(a.rotate(-1*alpha), b)) {
			return alpha;
		} else {
			return -1*alpha;
		}
	}
	
	/**
	 * Returns a {@link String} representation of a vector in a format "[X, Y]".
	 * 
	 * @return {@link String} representation of a vector in a format "[X, Y]"
	 */
	public String toString() {
		return "[" + x + ", " + y + "]";
	}
	
	/**
	 * Returns an angle between a vector [0, 1] and a vector this method is being called on.
	 * 
	 * @return an angle between a vector [0, 1] and a vector this method is being called on
	 */
	public double toAngle() {
		return angle(new Vector(0, 1), this);
	}
}
